package com.ks.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.ks.common.Const;
import com.ks.common.Global;
import com.ks.util.EMailUtil;
import com.ks.util.StringUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

public class MonitorManager {

    private static final org.apache.log4j.Logger log =  Logger.getLogger(MonitorManager.class);

    private static volatile MonitorManager instance;

    public static MonitorManager getInstance(){
        if(null == instance){
            synchronized (MonitorManager.class){
                if(null == instance){
                    instance = new MonitorManager();
                }
            }
        }

        return instance;
    }

    private enum  Status{
        NONE,RUNNING,RERUNNING,STOPPED
    }

    private Map<Integer, StatService> statServiceMap = new ConcurrentHashMap<>();

    private List<Server> servers = Collections.synchronizedList(new ArrayList<>());

    private static final int SEND_DAILY_DELAY_SECOND=17280;

    public void start(){
        Global.exec.scheduleAtFixedRate(this::task,2000,5000, TimeUnit.MILLISECONDS);
    }

    public void init(JSONArray servers){

        List<Server> list = new ArrayList<>();
        if(null != servers && !servers.isEmpty()){
            for (int i = 0; i < servers.size(); i++) {
                JSONObject object = servers.getJSONObject(i);
                Server server = JSON.parseObject(object.toJSONString(), Server.class);
                if(server.status == 1){
                    list.add(server);
                }
            }
        }
        this.servers.clear();
        if(!list.isEmpty())this.servers.addAll(list);

    }

    private void task(){

        if(!Const.scheduleTask)return;
        for (int i = 0; i < servers.size(); i++) {
            final Server server = servers.get(i);
            if(server.status == 1){
                Global.exec.execute(()->task(server));
            }
        }
    }

    private void task(Server server) {

        if(null == server)return;
        String url=server.url;
        if(StringUtils.isBlank(url))return;
        long startTime = System.currentTimeMillis();
        boolean succ = get(url);
        record(server,succ,System.currentTimeMillis()-startTime);
    }

    public void dailyTask(){
        Iterator<Map.Entry<Integer, StatService>> it = statServiceMap.entrySet().iterator();
        while (it.hasNext()){
            StatService service = it.next().getValue();
            Status status = service.noticed?Status.STOPPED:Status.RUNNING;
            String content = service.getDailyContent(status);
            sendMail(getDailyEmailTitle(service.server.name,status),content,service.server.getQqEmail());
        }
    }


    private void record(Server server,boolean succ,long cost){

        if(null == server)return;
        StatService statService = statServiceMap.computeIfAbsent(server.id, s -> new StatService(server));
        statService.record(succ,cost);
    }

    private void notice(Server server,boolean succ){

        StatService service = statServiceMap.get(server.id);
        if(null == service)return;
        Status status = succ?Status.RUNNING:Status.STOPPED;
        String content = service.getContent(succ,status);
        log.info(content);
        if(!succ && service.failCount>= Const.failMaxCount && !service.noticed){
            sendMail(getEmailTitle(service.server.getName(),status),content,service.server.getQqEmail());
            service.noticed = true;
        }
    }

    private String getEmailTitle(String serverName,Status status){
        return serverName+"("+status.toString().toLowerCase()+")";
    }
    private String getDailyEmailTitle(String serverName,Status status){
        return serverName+"(daily-"+status.toString().toLowerCase()+")";
    }

    private class StatService{

        private Server server;
        private Date beginDate;

        private long maxCost;
        private long totalCost;
        private long recentCost;
        private long failCost;

        private int failCount;
        private int failTotalCount;
        private Date firstFailDate;

        private int succCount;
        private int totalCount;

        private boolean noticed;

        private StatService( Server server){
            this.server = server;
            beginDate = new Date();
        }

        private String format(Date date){
            return DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss");
        }

        private void record(boolean succ ,long cost){

            if(cost>maxCost)maxCost = cost;
            totalCost+=cost;
            recentCost=cost;
            totalCount++;
            if(succ){
                succCount++;
                if(failCount>0)failCount=0;
                if(failCost>0)failCost=0;
                if(noticed){
                    maxCost = cost;
                    sendMail(getEmailTitle(server.name,Status.RERUNNING),getContent(succ,Status.RERUNNING),server.getQqEmail());
                    noticed = false;
                }
            }else {

                failCost+=cost;
                if(failCount == 0)firstFailDate = new Date();
                failCount++;
                failTotalCount++;
                if(succCount>0)succCount=0;
            }

            notice(server,succ);
//            if(totalCount >= SEND_DAILY_DELAY_SECOND)
//                sendMail(getEmailTitle(server.name,Status.RUNNING),getDailyContent(),1);
        }

        private String getContent(boolean succ,Status status){
            //取消默认排序
            JSONObject object = JSONObject.parseObject("{}", Feature.OrderedField);
            object.put("status",status.toString().toLowerCase());
            object.put("name",server.name);
            JSONObject cost = new JSONObject();
            object.put("cost",cost);
            cost.put("max",maxCost+" ms");
            cost.put("avg",getAvgCount(succ)+" ms");
            cost.put("rec",recentCost+" ms");

            object.put("bgt",format(beginDate));
            if(status.ordinal() == 3  && null != firstFailDate)
                object.put("fft",format(firstFailDate));
            return object.toJSONString();
        }

        private String getDailyContent(Status status){

            //取消默认排序
            JSONObject object = JSONObject.parseObject("{}", Feature.OrderedField);
            object.put("name",server.name);
            object.put("status",status.toString().toLowerCase());

            JSONObject cost = new JSONObject();
            object.put("cost",cost);
            cost.put("max",maxCost+" ms");
            cost.put("avg",getTotalAvgCount()+" ms");
            cost.put("rec",recentCost+" ms");

            object.put("bgt",format(beginDate));
            if(null != firstFailDate)object.put("fft",format(firstFailDate));
            object.put("ftc",failTotalCount);
            object.put("tc",totalCount);
            reset();
            return object.toJSONString();
        }

        private void reset(){
            firstFailDate = null;
            maxCost = 0;
            failTotalCount = 0;
            totalCost = 0;
            totalCount = 0;
        }

        private long getAvgCount(boolean succ){
            return succ?(totalCount ==0 ?0:totalCost/totalCount):(failCount==0?0:failCost/failCount);
        }
        private long getTotalAvgCount(){
            return totalCount ==0 ?0:totalCost/totalCount;
        }
    }

    @Getter @Setter
    public static class Server{
        private int id;
        private String name;
        private String url;
        private String qqEmail;
        private int status;
        public Server() { }
    }

    private boolean get(String httpUrl) {
        HttpURLConnection connection=null;
        try {
            // 创建远程url连接对象
            URL url = new URL(httpUrl);
            // 通过远程url连接对象打开一个连接，强转成httpURLConnection类
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接方式：get
            connection.setRequestMethod("GET");
            // 设置连接主机服务器的超时时间：
            connection.setConnectTimeout(2000);
            // 设置读取远程返回的数据时间：
            connection.setReadTimeout(2000);
            // 发送请求
            connection.connect();
            // 通过connection连接，获取输入流
            if (connection.getResponseCode() == 200) {
                return true;
            }
        } catch (IOException e) {
//            log.error("request url error");
        } finally {
            // 关闭远程连接
            if(null != connection)connection.disconnect();
        }
        return false;
    }

    /**
     *
     * @param emailTitle
     * @param emailContent
     */
    private void sendMail(String emailTitle,String emailContent,String teamQQEmail){

        log.info(emailContent);
        if(StringUtils.isBlank(teamQQEmail))return;
        List<String> emailAddresses = StringUtil.explode(",",teamQQEmail);
        if(null == emailAddresses || emailAddresses.size() ==0) return;
        emailTitle =StringUtils.isBlank(emailContent)?"server":emailTitle;
        long sendTime = System.currentTimeMillis();
        for (int i = 0; i < emailAddresses.size(); i++) {
            try {
                EMailUtil.sendEmail(emailAddresses.get(i),emailTitle,emailContent,sendTime);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("send mail error");
            }
        }
    }
}
